package com.damagez.selfrefresh.redis;

import com.damagez.selfrefresh.util.RefreshCacheThreadPool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.util.MethodInvoker;

import java.lang.reflect.InvocationTargetException;
import java.util.Set;

/**
 * @author damage
 * @create 2020/5/9 10:31
 * @description
 */
@Slf4j
public class RefreshRedisCache extends RedisCache {

    public static final Integer DEFAULT_INVOKE_COUNT = 10;

    private RefreshRedisCacheWriter cacheWriter;
    /**
     * 缓存剩下小于或等于毫秒数时自动刷新缓存
     */
    private Long refreshMill;

    /**
     * 刷新缓存要求的最小调用次数
     */
    private Integer refreshNeedMinInvoke = DEFAULT_INVOKE_COUNT;

    /**
     * Create new {@link RedisCache}.
     *
     * @param name        must not be {@literal null}.
     * @param cacheWriter must not be {@literal null}.
     * @param cacheConfig must not be {@literal null}.
     */
    protected RefreshRedisCache(String name, RefreshRedisCacheWriter cacheWriter, RedisCacheConfiguration cacheConfig, Long refreshMill, Integer refreshNeedMinInvoke) {
        super(name, cacheWriter, cacheConfig);
        this.cacheWriter = cacheWriter;
        this.refreshMill = refreshMill;
        if (refreshNeedMinInvoke != null) {
            this.refreshNeedMinInvoke = refreshNeedMinInvoke;
        }
    }

    @Override
    public ValueWrapper get(Object key) {
        //在redis中获得key的缓存
        ValueWrapper valueWrapper = super.get(key);
        Set<RefreshCacheCollectAspect.CachedInvocation> cachedInvocations = RefreshCacheCollectAspect.cacheToInvocationsMap.get(getName());
        for (RefreshCacheCollectAspect.CachedInvocation cachedInvocation : cachedInvocations) {
            Integer invokeCount = cachedInvocation.increaseInvokeCount();
            if (cachedInvocation.getKey().equals(key) && valueWrapper != null && invokeCount >= refreshNeedMinInvoke) {
                //缓存被调用超过最小阀值
                byte[] keyBytes = serializeCacheKey(createCacheKey(key));
                Long expire = cacheWriter.getExpire(getName(), keyBytes);
                if (expire != null && expire > 0 && expire <= refreshMill) {
                    //超时剩余时间已经小于刷新时间，启动线程调用方法刷新缓存
                    RefreshCacheThreadPool.getInstance().execute(() -> {
                        if (log.isDebugEnabled()) {
                            log.debug("自动刷新缓存key:{} expire:{} refreshMill:{} refreshNeedMinInvoke:{} invokeCount:{}", key, expire, refreshMill, refreshNeedMinInvoke, invokeCount);
                        }
                        boolean invokeSuccess;
                        byte[] valueBytes = null;
                        try {
                            final MethodInvoker invoker = new MethodInvoker();
                            invoker.setTargetObject(cachedInvocation.getTargetBean());
                            invoker.setArguments(cachedInvocation.getArguments());
                            invoker.setTargetMethod(cachedInvocation.getTargetMethod().getName());
                            invoker.prepare();
                            valueBytes = serializeCacheValue(invoker.invoke());
                            invokeSuccess = true;
                        } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                            log.error("缓存刷新失败！");
                            invokeSuccess = false;
                        }
                        if (invokeSuccess) {
                            cacheWriter.put(getName(), keyBytes, valueBytes, RefreshRedisCacheManager.getExpireTime(getName()));
                        }
                        cachedInvocation.resetInvokeCount();
                    });
                }
            }
        }
        return valueWrapper;
    }
}
